package com.yfjin.merge;

import android.annotation.SuppressLint;
import android.content.Context;
import android.media.AudioFormat;
import android.media.MediaCodec;
import android.media.MediaCodecInfo;
import android.media.MediaExtractor;
import android.media.MediaFormat;
import android.media.MediaMuxer;
import android.os.Environment;
import android.util.Log;

import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.nio.ByteBuffer;
import java.nio.channels.FileChannel;

class MusicProgress {

    private static int TIMEOUT = 1000;

    private static int WAIT_INPUT_TIME_US = 1000;
    private static int WAIT_OUTPUT_TIME_US = 1000;

    private int selectTrack(MediaExtractor mediaExtractor, boolean isAudio) {
        //获取每条轨道
        int numTracks = mediaExtractor.getTrackCount();
        for (int i = 0; i < numTracks; i++) {
            MediaFormat format = mediaExtractor.getTrackFormat(i);
            String mime = format.getString(MediaFormat.KEY_MIME);

            if (isAudio) {
                if (mime.startsWith("audio/")) {
                    return i;
                }
            } else {
                if (mime.startsWith("video/")) {
                    return i;
                }
            }

        }
        return -1;
    }


    public void mixAudioTrack(Context context, String videoPath, String audioPath, String outPath, int videoVolume, int audioVolume) throws Exception {

        int startTime = 20 * 1000 * 1000;
        int endTime = 35 * 1000 * 1000;

        File videoPcmFile = new File(Environment.getExternalStorageDirectory(), "input.pcm");
        if (videoPcmFile.exists()) {
            videoPcmFile.delete();
        }
        decode2Pcm(videoPath, videoPcmFile.getPath(), startTime, endTime);


        File musicPcmFile = new File(Environment.getExternalStorageDirectory(), "music.pcm");
        if (musicPcmFile.exists()) {
            musicPcmFile.delete();
        }
        decode2Pcm(audioPath, musicPcmFile.getPath(), startTime, endTime);

        String outPcmFile = new File(Environment.getExternalStorageDirectory(), "out.pcm").getPath();
        mixPcm(videoPcmFile.getPath(), musicPcmFile.getPath(), outPcmFile, 100, 100);


        File wavFile = new File(Environment.getExternalStorageDirectory(), "out.wav");
        if (wavFile.exists()) {
            wavFile.delete();
        }

        new PcmToWavUtil(44100, AudioFormat.CHANNEL_IN_STEREO,
                2, AudioFormat.ENCODING_PCM_16BIT)
                .pcmToWav(outPcmFile, wavFile.getAbsolutePath()
                );

        Log.i("123123", "音频混音完成!");

        videoPcmFile.delete();
        musicPcmFile.delete();
        new File(outPcmFile).delete();

        mixVideoAndMusic(videoPath, wavFile, outPath, startTime, endTime);
        Log.i("123123", "视频音频混音完成!");
    }


    @SuppressLint("WrongConstant")
    public void decode2Pcm(String musicPath, String outPath, int startTime, int endTime) throws Exception {
        if (endTime < startTime) {
            return;
        }
        File pcmFile = new File(outPath);
        FileChannel writeChannel = new FileOutputStream(pcmFile).getChannel();

        MediaExtractor mediaExtractor = new MediaExtractor();
        mediaExtractor.setDataSource(musicPath);
        int audioTrackIndex = selectTrack(mediaExtractor, true);
        mediaExtractor.selectTrack(audioTrackIndex);
        mediaExtractor.seekTo(startTime, MediaExtractor.SEEK_TO_CLOSEST_SYNC);

        MediaFormat mediaFormat = mediaExtractor.getTrackFormat(audioTrackIndex);

        int maxBufferSize = 100 * 1024;
        if (mediaFormat.containsKey(MediaFormat.KEY_MAX_INPUT_SIZE)) {
            maxBufferSize = mediaFormat.getInteger(MediaFormat.KEY_MAX_INPUT_SIZE);
        }


        ByteBuffer buffer = ByteBuffer.allocate(maxBufferSize);

        //"audio/aac" "audio/mmpg"
        MediaCodec mediaCodec = MediaCodec.createDecoderByType(mediaFormat.getString(MediaFormat.KEY_MIME));
        mediaCodec.configure(mediaFormat, null, null, 0);

        mediaCodec.start();
        MediaCodec.BufferInfo info = new MediaCodec.BufferInfo();
        int outputBufferIndex = -1;
        int count = 0;
        while (true) {
            count++;
            Log.i("123123", "aa count:" + count);
            int decodeInputIndex = mediaCodec.dequeueInputBuffer(WAIT_INPUT_TIME_US);
            if (decodeInputIndex >= 0) {
                long sampleTimeUs = mediaExtractor.getSampleTime();
                if (sampleTimeUs == -1) {
                    break;
                } else if (sampleTimeUs < startTime) {
                    //丢掉 不用了
                    mediaExtractor.advance();
                    continue;
                } else if (sampleTimeUs > endTime) {
                    break;
                }
                info.size = mediaExtractor.readSampleData(buffer, 0);
                info.presentationTimeUs = sampleTimeUs;
                info.flags = mediaExtractor.getSampleFlags();
                byte[] content = new byte[buffer.remaining()];
                buffer.get(content);
                ByteBuffer inputBuffer = mediaCodec.getInputBuffer(decodeInputIndex);
                inputBuffer.put(content);
                mediaCodec.queueInputBuffer(decodeInputIndex, 0, info.size, info.presentationTimeUs, info.flags);
                mediaExtractor.advance();
            }

            outputBufferIndex = mediaCodec.dequeueOutputBuffer(info, WAIT_OUTPUT_TIME_US);
            while (outputBufferIndex >= 0) {
//                Log.i("123123", "bb count:" + count);
                ByteBuffer decodeOutputBuffer = mediaCodec.getOutputBuffer(outputBufferIndex);
                writeChannel.write(decodeOutputBuffer);//MP3  1   pcm2
                mediaCodec.releaseOutputBuffer(outputBufferIndex, false);
                outputBufferIndex = mediaCodec.dequeueOutputBuffer(info, WAIT_OUTPUT_TIME_US);
            }


        }

        writeChannel.close();
        mediaExtractor.release();
        mediaCodec.stop();
        mediaCodec.release();

        Log.i("123123", outPath + " TO PCM: 转换完毕");
    }


    public void mixPcm(String pcm1Path, String pcm2Path, String outPutPath, int vol1, int vol2) throws IOException {

        byte[] buffer1 = new byte[2048];
        byte[] buffer2 = new byte[2048];
        byte[] buffer3 = new byte[2048];

        FileInputStream is1 = new FileInputStream(pcm1Path);
        FileInputStream is2 = new FileInputStream(pcm2Path);

        FileOutputStream os = new FileOutputStream(outPutPath);

        boolean end1 = false;
        boolean end2 = false;


        short temp1, temp2;
        //两个short相加会大于short，所以定义int
        int temp;


        int mergeCount = 0;

        while (!end1 || !end2) {

            mergeCount++;
            Log.i("123123", "mergeCount:" + mergeCount);

            if (!end1) {
                end1 = is1.read(buffer1) == -1;
                System.arraycopy(buffer1, 0, buffer3, 0, buffer1.length);
            }

            if (!end2) {
                end2 = is2.read(buffer2) == -1;
                int voice = 0;

                //+2 一个声音两个字节，所以+2  低8位 前 ，高8位 后
                for (int i = 0; i < buffer2.length; i += 2) {
                    temp1 = (short) ((buffer1[i] & 0xff) | ((buffer1[i + 1] & 0xff) << 8));
                    temp2 = (short) ((buffer2[i] & 0xff) | ((buffer2[i + 1] & 0xff) << 8));

                    temp = temp1 * vol1 / 100 + temp2 * vol1 / 100;

                    if (temp > Short.MAX_VALUE) {
                        temp = Short.MAX_VALUE;
                    } else if (temp < Short.MIN_VALUE) {
                        temp = Short.MIN_VALUE;
                    }

                    buffer3[i] = (byte) (temp & 0XFF);
                    buffer3[i + 1] = (byte) ((temp >>> 8) & 0XFF);

                }
                os.write(buffer3);
            }
        }

        is1.close();
        is2.close();
        os.close();

        Log.i("123123", "mergeFinish");

    }

    //=======================================================================================================

    private void mixVideoAndMusic(String videoInput, File wavFile, String output, int startTimeUs, int endTimeUs) throws IOException {



        //视频封装器
        MediaMuxer mediaMuxer = new MediaMuxer(output, MediaMuxer.OutputFormat.MUXER_OUTPUT_MPEG_4);


        //视频轨道
        MediaExtractor mediaExtractor = new MediaExtractor();
        mediaExtractor.setDataSource(videoInput);
        int videoIndex = selectTrack(mediaExtractor, false);
        int audioIndex = selectTrack(mediaExtractor, true);

        MediaFormat videoFormat = mediaExtractor.getTrackFormat(videoIndex);
        mediaMuxer.addTrack(videoFormat);
        //视频的音频轨道
        MediaFormat videoAudioFormat=mediaExtractor.getTrackFormat(audioIndex);
        int audioBitrate = videoAudioFormat.getInteger(MediaFormat.KEY_BIT_RATE);
        videoAudioFormat.setString(MediaFormat.KEY_MIME, MediaFormat.MIMETYPE_AUDIO_AAC);
//        int totalTime = videoAudioFormat.getInteger(MediaFormat.KEY_DURATION);
        int muxerAudioIndex = mediaMuxer.addTrack(videoAudioFormat);

        mediaMuxer.start();


        //音频的wav
        MediaExtractor pcmExtroator = new MediaExtractor();
        pcmExtroator.setDataSource(wavFile.getAbsolutePath());
        int audioTrack =  selectTrack(pcmExtroator, true);
        pcmExtroator.selectTrack(audioTrack);
        MediaFormat pcmTrackFormat = pcmExtroator.getTrackFormat(audioTrack);
        int pcmMaxBufferSize=pcmTrackFormat.getInteger(MediaFormat.KEY_MAX_INPUT_SIZE);
        Log.i("123123","wav音频-------------KEY_MAX_INPUT_SIZE:"+pcmMaxBufferSize);


        Log.i("123123","音频轨道合成开始...");
        //==============================================================================
        //音频

//        int audioMaxBufferSize = 100 * 1000;
//        if (videoAudioFormat.containsKey(MediaFormat.KEY_MAX_INPUT_SIZE)) {
//            audioMaxBufferSize = videoAudioFormat.getInteger(MediaFormat.KEY_MAX_INPUT_SIZE);
//        }
//        Log.i("123123","shi音频-------------KEY_MAX_INPUT_SIZE:"+audioMaxBufferSize);


        MediaFormat encodeFormat = MediaFormat.createAudioFormat(MediaFormat.MIMETYPE_AUDIO_AAC, 44100, 2);
        encodeFormat.setInteger(MediaFormat.KEY_BIT_RATE, audioBitrate);
        encodeFormat.setInteger(MediaFormat.KEY_AAC_PROFILE, MediaCodecInfo.CodecProfileLevel.AACObjectERLC);
        encodeFormat.setInteger(MediaFormat.KEY_MAX_INPUT_SIZE, pcmMaxBufferSize);
        MediaCodec encoder = MediaCodec.createEncoderByType(MediaFormat.MIMETYPE_AUDIO_AAC);
        encoder.configure(encodeFormat, null, null, MediaCodec.CONFIGURE_FLAG_ENCODE);
        encoder.start();



        ByteBuffer buffer = ByteBuffer.allocate(pcmMaxBufferSize);
        MediaCodec.BufferInfo info = new MediaCodec.BufferInfo();
        boolean encodeDone = false;

        int audioCount=0;
        while (!encodeDone) {
            audioCount++;
            Log.i("123123","音频:"+audioCount);

            int inputBufferIndex = encoder.dequeueInputBuffer(WAIT_INPUT_TIME_US);
            if (inputBufferIndex >= 0) {

                long sampleTime = pcmExtroator.getSampleTime();
                if (sampleTime < 0) {
                    //pts小于0，来到了文件末尾，不用继续编码
                    encoder.queueInputBuffer(inputBufferIndex, 0, 0, 0, MediaCodec.BUFFER_FLAG_END_OF_STREAM);
                } else {
                    int flag = pcmExtroator.getSampleFlags();
                    int size = pcmExtroator.readSampleData(buffer, 0);

                    ByteBuffer inputBuffer = encoder.getInputBuffer(inputBufferIndex);
                    inputBuffer.clear();
                    inputBuffer.put(buffer);
                    inputBuffer.position(0);
                    encoder.queueInputBuffer(inputBufferIndex, 0, size, sampleTime, flag);
                    //移动到下一帧
                    pcmExtroator.advance();
                }

            }
            int outputBufferIndex = encoder.dequeueOutputBuffer(info, TIMEOUT);
            while (outputBufferIndex >= 0) {
                if (info.flags == MediaCodec.BUFFER_FLAG_END_OF_STREAM) {
                    encodeDone = true;
                    break;
                }

                ByteBuffer encodeOutputBuffer = encoder.getOutputBuffer(outputBufferIndex);
                //写入轨道
                mediaMuxer.writeSampleData(muxerAudioIndex, encodeOutputBuffer, info);
                encodeOutputBuffer.clear();
                encoder.releaseOutputBuffer(outputBufferIndex, false);
                outputBufferIndex = encoder.dequeueOutputBuffer(info, TIMEOUT);
            }

        }

        Log.i("123123","视频轨道合成开始");
        //==================================================================
        //视频
        if (audioIndex > 0) {
            mediaExtractor.unselectTrack(audioIndex);
        }

        mediaExtractor.selectTrack(videoIndex);
        mediaExtractor.seekTo(startTimeUs, MediaExtractor.SEEK_TO_PREVIOUS_SYNC);
        int videoMaxBufferSize = videoFormat.getInteger(MediaFormat.KEY_MAX_INPUT_SIZE);
        buffer = ByteBuffer.allocate(videoMaxBufferSize);

        Log.i("123123","视频-------------KEY_MAX_INPUT_SIZE:"+videoMaxBufferSize);


        int videoCount=0;
        while (true) {
            videoCount++;
            Log.i("123123","视频:"+videoCount);

            long sampleTimeUs = mediaExtractor.getSampleTime();
            if (sampleTimeUs == -1) {
                break;
            }
            if (sampleTimeUs < startTimeUs) {
                mediaExtractor.advance();
                continue;
            }
            if (sampleTimeUs > endTimeUs) {
                break;
            }


            info.presentationTimeUs = sampleTimeUs - startTimeUs;
            info.flags = mediaExtractor.getSampleFlags();
            info.size = mediaExtractor.readSampleData(buffer, 0);

            if (info.size < 0) {
                break;
            }
            //写入轨道
            mediaMuxer.writeSampleData(videoIndex, buffer, info);
            mediaExtractor.advance();
        }


        pcmExtroator.release();
        mediaExtractor.release();
        encoder.stop();
        encoder.release();
        mediaMuxer.release();

    }
}
